package cn.zt.middleware.dynamic.thread.pool.sdk.config;

import cn.zt.middleware.dynamic.thread.pool.sdk.domain.DynamicThreadPoolService;
import cn.zt.middleware.dynamic.thread.pool.sdk.domain.IDynamicThreadPoolService;
import cn.zt.middleware.dynamic.thread.pool.sdk.domain.model.entity.ThreadPoolConfigEntity;
import cn.zt.middleware.dynamic.thread.pool.sdk.domain.model.valobj.RegistryEnumVO;
import cn.zt.middleware.dynamic.thread.pool.sdk.registry.IRegistry;
import cn.zt.middleware.dynamic.thread.pool.sdk.registry.redis.RedisRegistry;
import cn.zt.middleware.dynamic.thread.pool.sdk.trigger.job.ThreadPoolDataReportJob;
import cn.zt.middleware.dynamic.thread.pool.sdk.trigger.listener.ThreadPoolConfigAdjustListener;
import org.apache.commons.lang.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author zhangtao
 * @Description 动态线程配置入口
 * @date 2020/5/28 16:08
 */

@Configuration
@EnableConfigurationProperties(DynamicThreadPoolAutoProperties.class)
@EnableScheduling
public class DynamicThreadPoolAutoConfig {

    private final Logger logger = LoggerFactory.getLogger(DynamicThreadPoolAutoConfig.class);

    private String applicationName;

    /**
     * 注册动态线程池服务
     */
    @Bean("dynamicThreadPoolService")
    public DynamicThreadPoolService dynamicThreadPoolService(ApplicationContext context, Map<String, ThreadPoolExecutor> threadPoolExecutorMap,
                                                             RedissonClient dynamicThreadPoolRedissonClient) {
        applicationName = context.getEnvironment().getProperty("spring.application.name");

        if (StringUtils.isBlank(applicationName)) {
            applicationName = "缺省的";
            logger.error("动态线程池服务启动失败,SpringBoot 应用未配置 spring.application.name 无法获取应用名称");
        }

        // 通过获取注册中心配置数据，设置线程池配置信息
        Set<String> threadPoolKeys = threadPoolExecutorMap.keySet();
        for (String threadPoolKey : threadPoolKeys) {
            ThreadPoolConfigEntity entity = dynamicThreadPoolRedissonClient.<ThreadPoolConfigEntity>getBucket(RegistryEnumVO.THREAD_POOL_CONFIG_LIST_KEY.getKey() + "_"
                                            + applicationName + "_" + threadPoolKeys).get();
            if (null == entity) {
                continue;
            }
            ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolKey);
            threadPoolExecutor.setCorePoolSize(entity.getCorePoolSize());
            threadPoolExecutor.setMaximumPoolSize(entity.getMaximumPoolSize());
        }
        return new DynamicThreadPoolService(applicationName, threadPoolExecutorMap);
    }

    /**
     * 注册redisson客户端
     */
    @Bean("dynamicThreadPoolRedissonClient")
    public RedissonClient redissonClient(DynamicThreadPoolAutoProperties properties) {
        Config config = new Config();
        // 设置redisson编码器
        // 根据需要可以设定编解码器；https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96
        config.setCodec(JsonJacksonCodec.INSTANCE);

        // init redisson
        config.useSingleServer()
                .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
                .setPassword(properties.getPassword())
                .setConnectionPoolSize(properties.getPoolSize())
                .setConnectionMinimumIdleSize(properties.getMinIdleSize())
                .setIdleConnectionTimeout(properties.getIdleTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setRetryAttempts(properties.getRetryAttempts())
                .setRetryInterval(properties.getRetryInterval())
                .setPingConnectionInterval(properties.getPingInterval())
                .setKeepAlive(properties.isKeepAlive())
        ;
        RedissonClient redissonClient = Redisson.create(config);

        logger.info("动态线程池注册中心(redis)初始化成功 {} {} {}", properties.getHost(), properties.getPort(), !redissonClient.isShutdown());
        return redissonClient;
    }

    /**
     * 注册redis注册中心
     */
    @Bean("dynamicThreadPoolRedisRegistry")
    public IRegistry redisRegistry(RedissonClient dynamicThreadPoolRedissonClient) {
        return new RedisRegistry(dynamicThreadPoolRedissonClient);
    }

    /**
     * 注册动态线程池数据上报任务组件
     */
    @Bean("dynamicThreadPoolDataReportJob")
    public ThreadPoolDataReportJob threadPoolDataReportJob(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry dynamicThreadPoolRedisRegistry) {
        return new ThreadPoolDataReportJob(dynamicThreadPoolService, dynamicThreadPoolRedisRegistry);
    }

    /**
     * 注册动态线程池配置监听组件
     */
    @Bean("threadPoolConfigAdjustListener")
    public ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry dynamicThreadPoolRedisRegistry) {
        return new ThreadPoolConfigAdjustListener(dynamicThreadPoolService, dynamicThreadPoolRedisRegistry);
    }

    /**
     * 注册主题对象
     */
    @Bean(name = "dynamicThreadPoolRedisTopic")
    public RTopic threadPoolConfigAdjustListener(RedissonClient dynamicThreadPoolRedissonClient, ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener) {
        // 获取订阅主题
        RTopic topic = dynamicThreadPoolRedissonClient.getTopic(RegistryEnumVO.DYNAMIC_THREAD_POOL_REDIS_TOPIC.getKey() + "_" + applicationName);
        topic.addListener(ThreadPoolConfigEntity.class, threadPoolConfigAdjustListener);
        return topic;
    }
}
